import static java.lang.StrictMath.PI;
import static java.lang.StrictMath.abs;
import model.ActionType;
import model.Game;
import model.Hockeyist;
import model.HockeyistState;
import model.Move;
import model.Unit;
import model.World;

/**
 *
 * @author Constantine Tretyak
 */
public class Attack extends AbstractStrategy {

    private static final double STRIKE_ANGLE = 0.1D * PI / 180.0D;

    private Point center = new Point(5, coords.getXcoord(0), coords.getYcoord(0));

    public Attack(Hockeyist self, World world, Game game, Move move) {
        super(self, world, game, move);
    }

    @Override
    public void run() {
        if (self.getState() == HockeyistState.SWINGING && (self.getSwingTicks() >= WAIT_SWING_TICKS || self.getSwingTicks() >= game.getMaxEffectiveSwingTicks())) {
            move.setAction(ActionType.STRIKE);
            if (self.getRemainingCooldownTicks() == 0) {
                BACK_AFTER_SHOOT_TICKS = 100 * world.getHockeyists().length / 2;
            }
            return;
        }

        if (hasPuck(self)) {
            // бить или не бить
            for (double aStrike = 1.0d; aStrike >= -1.0d; aStrike -= 0.2d) {
                boolean shooting = false;
                boolean swinging = false;
                if (selfX < 0.0d) {
                    shooting = shootingCondition(self, aStrike);
                    swinging = swingingCondition(self, aStrike);
                }
                if (getOppsAround(getNet(false), 300).size() > 0 && swinging) {
                    move.setSpeedUp(aStrike);
                    move.setTurn(self.getAngleTo(center));
                    return;
                }
                if (getOppsAround(getNet(false), 300).size() < 2 && shooting) {
                    move.setSpeedUp(aStrike);
                    shooting();
                    return;
                }
                if (swinging) {
                    move.setSpeedUp(aStrike);
                    move.setTurn(self.getAngleTo(center));
                    return;
                }
            }

        } else {
            if (self.getTeammateIndex() == WAIT_PASS_TM_INDEX) {
                readyToGetPass();
                WAIT_PASS_TM_INDEX = -1;
                return;
            }
        }

        // Определяем тиммейтов в зависмости от положения
        long puckOwner = puck.getOwnerHockeyistId();
        long first = -1;
        for (Unit unit : getTeammates(puck)) {
            if (puckOwner != unit.getId()) {
                first = unit.getId();
                break;
            }
        }

        Unit puckOwnerUnit = getUnitWithPuck();

        // Определяем отдать пасс открытом тиммейту или нет
        Unit nearOppFront = !getOppsFront(self, coords.getWidth() / 3).isEmpty() ? getOppsFront(self, coords.getWidth() / 3).get(0) : null;
        if (nearOppFront != null && puckOwnerUnit.getId() == self.getId()
                && (abs(nearOppFront.getAngleTo(self)) < 10 * PI / 180 || abs(nearOppFront.getAngleTo(puck)) < 10 * PI / 180)
                && moving.getSpeed(nearOppFront) > 1.0d
                && self.getDistanceTo(nearOppFront) < coords.getWidth() / 3
                && getOppsAround(getUnitById(first), game.getStickLength()).isEmpty()
                && getUnitById(first).getDistanceTo(self) > coords.getHeight() / 2) {
            pass(getUnitById(first));
            return;
        }

        if (coords.getX(puckOwnerUnit.getX()) < -0.7d) {    // если владелец шайбы в мертвой зоне
            new DeadZoneAttack(self, world, game, move, puckOwner, first);
            return;
        }

        if (coords.getX(puckOwnerUnit.getX()) > 0.0d) { // определяем, медленная ли это атака. Это когда противник в глухой обороне и не хочет наступать
            boolean slowAttack = true;
            for (Unit opp : getOpponents(self)) {
                if (coords.getX(opp.getX()) > -0.5d) {
                    slowAttack = false;
                    break;
                }
            }
            if (slowAttack) {
                new SlowAttack(self, world, game, move, puckOwner, first);
                return;
            }
        }
        new DefaultAttack(self, world, game, move, puckOwner, first);
    }

    public boolean shootingCondition(Hockeyist hockeyist, double aStrike) {
        double ticks = 0;
        Unit turningH = moving.cast(hockeyist, getNet(false), aStrike, ticks);
        while (ticks < 100) {
            if (abs(turningH.getAngleTo(getNet(false))) < STRIKE_ANGLE) {
                if (shootPuckToNet(turningH, 0)) {
                    return true;
                }
            } else {
                turningH = moving.cast(turningH, getNet(false), 0.0d, 1);
            }
            ticks++;
        }
        return false;
    }

   

    private void readyToGetPass() {
        move.setTurn(self.getAngleTo(puck));
        if (abs(self.getAngleTo(puck)) < 0.5 * PI / 180) {
            move.setSpeedUp(-0.1d);
        }
    }

    private boolean shootPuckToNet(Unit puckOwner, double swingTics) {
        if (getGoalie(false) == null) {
            return true;
        }
        if (coords.getX(puckOwner.getX()) < -0.7d) {
            return false;
        }
        double fPuckX = puckOwner.getX() + Math.cos(puckOwner.getAngle()) * game.getPuckBindingRange();
        double fPuckY = puckOwner.getY() + Math.sin(puckOwner.getAngle()) * game.getPuckBindingRange();
        double fPuckSpeed = 20 * game.getStrikePowerBaseFactor() + swingTics * game.getStrikePowerGrowthFactor() + moving.getSpeed(puckOwner) * Math.cos(puckOwner.getAngle() - Math.atan2(puckOwner.getSpeedY(), puckOwner.getSpeedX()));
        Unit puckBeforeShoot = copyUnit(puck, fPuckX, fPuckY, fPuckSpeed * Math.cos(puckOwner.getAngle()), fPuckSpeed * Math.sin(puckOwner.getAngle()));
        Unit goalie = getGoalie(false);
        double goalieY = puckBeforeShoot.getY();
        if (goalieY < 390.0d) {
            goalieY = 390d;
        }
        if (goalieY > 530.0d) {
            goalieY = 530d;
        }
        goalie = copyUnit(goalie, goalie.getX() + (goalie.getX() > 500 ? -goalie.getRadius() : goalie.getRadius()), goalieY, goalie.getSpeedX(), goalie.getSpeedY());
        double ticks = 0;
        while (coords.getX(puckBeforeShoot.getX()) > coords.getX(goalie.getX()) && moving.getSpeed(puckBeforeShoot) > 0.5) {
            if (ticks > 30) {
                return false;
            }
            goalieY += Math.signum(puckBeforeShoot.getY() - goalie.getY()) * game.getGoalieMaxSpeed();
            if (goalieY < 390.0d) {
                goalieY = 390d;
            }
            if (goalieY > 530.0d) {
                goalieY = 530d;
            }
            puckBeforeShoot = moving.cast(puckBeforeShoot, null, 0.0d, 1);
            if (puckBeforeShoot.getDistanceTo(goalie) < puckBeforeShoot.getRadius() + 7) {
                return false;
            }
            goalie = copyUnit(goalie, goalie.getX(), goalieY, goalie.getSpeedX(), goalie.getSpeedY());
            ticks++;
        }
        return puckBeforeShoot.getDistanceTo(goalie) > puckBeforeShoot.getRadius() + 7;
    }

    private void shooting() {

        double angleToNet = self.getAngleTo(getNet(false).getX(), getNet(false).getY());
        move.setTurn(angleToNet);

        if (abs(angleToNet) < STRIKE_ANGLE) {
            move.setAction(ActionType.STRIKE);
            if (self.getRemainingCooldownTicks() == 0) {
                BACK_AFTER_SHOOT_TICKS = 100;
            }
        }
    }

    private boolean swingingCondition(Hockeyist hockeyist, double aStrike) {
        double ticks = 0;
        Unit turningH = moving.cast(hockeyist, center, aStrike, ticks);
        while (ticks < 100 && abs(turningH.getAngle()) > PI / 2) {
            for (int swingTicks = 10; swingTicks < game.getMaxEffectiveSwingTicks(); swingTicks++) {
                Unit unitFromSwingTics = moving.cast(turningH, null, 0.0d, swingTicks);
                if (abs(unitFromSwingTics.getAngleTo(getNet(false))) < STRIKE_ANGLE) {
                    if (shootPuckToNet(unitFromSwingTics, swingTicks)) {
                        if (ticks == 0) {
                            WAIT_SWING_TICKS = swingTicks++;
                            move.setAction(ActionType.SWING);
                        } else {
                            return true;
                        }
                    }
                }
            }

            turningH = moving.cast(hockeyist, center, aStrike, ticks);
            ticks++;
        }
        return false;

    }

}
